{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "2c000cd3-4871-4494-b717-db7190ccdbfe",
   "metadata": {},
   "source": [
    "# Tests for Husky PLL phase consistency.\n",
    "\n",
    "Checks whether relative phase between target and ADC clocks is consistent: i.e. whether a given set of `scope.clock` parameters always produces the same phase.\n",
    "\n",
    "Covers issues [490](https://github.com/newaetech/chipwhisperer/issues/490) and [499](https://github.com/newaetech/chipwhisperer/issues/499).\n",
    "\n",
    "Does NOT check whether the phase is correct (e.g. [issue 501](https://github.com/newaetech/chipwhisperer/issues/501) -- that's covered in the part 2 notebook.\n",
    "\n",
    "Should be run whenever changes are made to `ChipWhispererHuskyClocks.py`.\n",
    "\n",
    "Could be a `pytest` script, however the ability to plot the clocks and visualize can be really useful when debugging."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "437412e7-a94c-4b87-8c09-d56cb408638b",
   "metadata": {},
   "outputs": [],
   "source": [
    "import chipwhisperer as cw"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f10d42da-fbdf-4f3d-96ac-3a99a4ffa337",
   "metadata": {},
   "outputs": [],
   "source": [
    "scope = cw.scope()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a42bc734-b054-4e60-aa6f-202b7e7fda13",
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.default_setup()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6d7196f4-d863-4d49-a6ae-a0b7961e2080",
   "metadata": {},
   "source": [
    "# There are two cases to cover:\n",
    "1. `EXTCLK = True`: target-supplied clock, from a CW305. Could be adapted to use a different target, but we need to be able to set its clock over a wide range.\n",
    "2. `EXTCLLK = False`: no target needed; PLL uses 12 MHz XTAL reference."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c549df54-2cfb-48b8-ab92-ee9089c5bbe9",
   "metadata": {},
   "outputs": [],
   "source": [
    "#EXTCLK = True\n",
    "EXTCLK = False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "29491931-9140-4b10-b964-fe43230cac86",
   "metadata": {},
   "outputs": [],
   "source": [
    "if EXTCLK:\n",
    "    # setting PLL won't work without a bitfile?\n",
    "    target = cw.target(scope, cw.targets.CW305, fpga_id='100t')\n",
    "    target.pll.pll_enable_set(True)\n",
    "    target.pll.pll_outenable_set(False, 0)\n",
    "    target.pll.pll_outenable_set(True, 1)\n",
    "    target.pll.pll_outenable_set(False, 2)\n",
    "    target.pll.pll_outfreq_set(10e6, 1)\n",
    "    MAXCLOCK = 167e6\n",
    "    #MAXCLOCK = 99e6 # NOTE: if allow_rdiv = False, can't go above 100M!\n",
    "    scope.clock.clkgen_freq = 10e6\n",
    "    scope.clock.clkgen_src = 'extclk'\n",
    "else:\n",
    "    MAXCLOCK = 200e6\n",
    "    target = None\n",
    "    scope.clock.clkgen_src = 'system'\n",
    "\n",
    "\n",
    "OVERSAMP = 20\n",
    "scope.LA.enabled = True\n",
    "if EXTCLK:\n",
    "    scope.LA.clk_source = 'target'\n",
    "else:\n",
    "    scope.LA.clk_source = 'pll'\n",
    "scope.LA.clkgen_enabled = False\n",
    "scope.LA.oversampling_factor = OVERSAMP\n",
    "scope.LA.clkgen_enabled = True\n",
    "scope.LA.capture_group = 'CW 20-pin'\n",
    "scope.LA.capture_depth = 200 \n",
    "assert scope.LA.locked"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3e6add9a-a3ed-4ef6-abb8-cba9e9a7f091",
   "metadata": {},
   "source": [
    "When `EXTCLK = True`, there would be a bunch of `scope_logger.errors()` due to clock/mul settings that can't be achieved; this silences them so that the output isn't cluttered.\n",
    "\n",
    "For the same reason, we mute warnings.\n",
    "\n",
    "**If debugging, unset `scope.clock._quiet` to see all errors, and turn warnings back on!**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c63d406f-8c29-4db7-a594-f8d72f9536cc",
   "metadata": {},
   "outputs": [],
   "source": [
    "cw.scope_logger.setLevel(cw.logging.ERROR)\n",
    "#cw.scope_logger.setLevel(cw.logging.WARNING)\n",
    "\n",
    "if EXTCLK:\n",
    "    scope.clock._quiet = True\n",
    "else:\n",
    "    scope.clock._quiet = False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fb635cc5-d3ee-45a1-a473-daea3f7c8649",
   "metadata": {},
   "outputs": [],
   "source": [
    "def setup(freq, mul, phase, allow_rdiv=False, update_mul=True):\n",
    "    global all_settings\n",
    "    if EXTCLK:\n",
    "        target.pll.pll_outfreq_set(freq, 1)\n",
    "    # randomly order the mul/freq updates:\n",
    "    if random.randint(0,1):\n",
    "        if update_mul: scope.clock.adc_mul = mul\n",
    "        scope.clock.clkgen_freq = freq\n",
    "    else:\n",
    "        scope.clock.clkgen_freq = freq\n",
    "        if update_mul: scope.clock.adc_mul = mul\n",
    "    if phase is not None:\n",
    "        #print('applying provided phase: %f' % phase)\n",
    "        scope.clock.adc_phase = phase\n",
    "    else:\n",
    "        maxphase = scope.clock.pll.max_phase_percent\n",
    "        phase = random.random()*maxphase\n",
    "        if random.randint(0,1) and not EXTCLK:\n",
    "            phase = -phase\n",
    "        #print('applying chosen phase: %f' % phase)\n",
    "        scope.clock.adc_phase = phase\n",
    "    assert scope.clock.pll.pll_locked\n",
    "    setting = {}\n",
    "    setting['freq'] = freq\n",
    "    setting['mul'] = mul\n",
    "    setting['phase'] = phase\n",
    "    setting['allow_rdiv'] = allow_rdiv\n",
    "    setting['update_mul'] = update_mul\n",
    "    setting['pll_settings'] = str(scope.clock.pll)\n",
    "    setting['params'] = list(scope.clock.pll.parameters)\n",
    "    all_settings.append(setting)\n",
    "    return scope.clock.adc_phase\n",
    "\n",
    "def set_oversamp(oversamp):\n",
    "    scope.LA.clkgen_enabled = False\n",
    "    scope.LA.oversampling_factor = oversamp\n",
    "    scope.LA.clkgen_enabled = True\n",
    "    assert scope.LA.locked\n",
    "\n",
    "def get_fail_package(ID, FREQ, OVERSAMP, TOL, MUL, INPUT_PHASE, NEW_PHASE, NEWFREQ, NEWMUL, ref_phase, ref_adc, ref_ref, new_phase, new_adc, new_ref, ref_settings, new_settings, prev_settings, ref_sampling_clock, new_sampling_clock):\n",
    "    packed = {}\n",
    "    packed['ID'] = ID\n",
    "    packed['FREQ'] = FREQ\n",
    "    packed['OVERSAMP'] = OVERSAMP\n",
    "    packed['TOL'] = TOL\n",
    "    packed['MUL'] = MUL\n",
    "    packed['INPUT_PHASE'] = INPUT_PHASE\n",
    "    packed['NEW_PHASE'] = NEW_PHASE\n",
    "    packed['NEWFREQ'] = NEWFREQ\n",
    "    packed['NEWMUL'] = NEWMUL\n",
    "    packed['ref_phase'] = ref_phase\n",
    "    packed['new_phase'] = new_phase\n",
    "    packed['ref_settings'] = ref_settings\n",
    "    packed['new_settings'] = new_settings\n",
    "    packed['prev_settings'] = prev_settings\n",
    "    packed['ref_sampling_clock'] = ref_sampling_clock\n",
    "    packed['new_sampling_clock'] = new_sampling_clock\n",
    "\n",
    "    offset = find0to1trans(ref_ref)[0]\n",
    "    packed['ref_adc'] = ref_adc[offset:]\n",
    "    packed['ref_ref'] = ref_ref[offset:]\n",
    "\n",
    "    offset = find0to1trans(new_ref)[0]\n",
    "    packed['new_adc'] = new_adc[offset:]\n",
    "    packed['new_ref'] = new_ref[offset:]\n",
    "    return packed\n",
    "\n",
    "def find0to1trans(data):\n",
    "    pattern = [0,1]\n",
    "    return [i for i in range(0,len(data)) if list(data[i:i+len(pattern)])==pattern]\n",
    "\n",
    "def get_clocks(extclk=False):\n",
    "    done = False\n",
    "    count = 0\n",
    "    while not done and count < 30:\n",
    "        scope.LA.arm()\n",
    "        scope.LA.trigger_now()\n",
    "        raw = scope.LA.read_capture_data()\n",
    "        adcclock = scope.LA.extract(raw, 8)\n",
    "        if extclk:\n",
    "            refclock = scope.LA.extract(raw, 4)\n",
    "        else:\n",
    "            refclock = scope.LA.extract(raw, 5)\n",
    "        \n",
    "        edges = find0to1trans(refclock)\n",
    "        if len(edges) > 1:\n",
    "            ref_edge = edges[1]\n",
    "        else:\n",
    "            ref_edge = edges[0]    \n",
    "        \n",
    "        try:\n",
    "            #adc_ref_delta = find0to1trans(adcclock[ref_edge:])[0]\n",
    "            adc_edges = find0to1trans(adcclock)\n",
    "            adc_ref_delta = abs(min(adc_edges, key=lambda x:abs(x-ref_edge)) - ref_edge)\n",
    "            done = True\n",
    "        except:\n",
    "            # not sure why but sometimes the ADC clock comes back all zeros; could be an issue with the PLL or with the LA?\n",
    "            # what's very strange is that this doesn't happen often, but when it does, adcclock is always all zeros, and \n",
    "            # the capture is re-attempted exactly 19 times before it's successful!\n",
    "            if all(c == 0 for c in adcclock):\n",
    "                adcclock = 'all zeros'\n",
    "            print('could not find delta; ref_edge=%3d, lock status=%s; adcclock=%s; trying again' % (ref_edge, scope.clock.pll.pll_locked, adcclock))\n",
    "            assert scope.LA.locked\n",
    "            assert scope.clock.pll.pll_locked\n",
    "            time.sleep(0.5)\n",
    "            count += 1\n",
    "    return adc_ref_delta, adcclock, refclock"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e618a684-eeeb-4685-8657-4dc21691fa41",
   "metadata": {},
   "source": [
    "# Main Test Loop\n",
    "\n",
    "Note that here we do NOT concern ourselves at all with whether the phase is **correct**; all we care is whether the phase is **consistent**.\n",
    "\n",
    "The approach is:\n",
    "1. Pick a random (but valid) frequency and `adc_mul`; measure the clocks' relative phase.\n",
    "2. Change the clock frequency and `adc_mul` to other values, then return to the original setting from step 1 and check if the phase is the same that it was before.\n",
    "\n",
    "The `REPS` parameter detemines how long the test is. The loop runs at around 20 seconds per rep."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3cb8649a-5122-48fd-80b7-3e2115c953d2",
   "metadata": {},
   "outputs": [],
   "source": [
    "#REPS = 10\n",
    "REPS = 2000"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "69d0bd31-1854-428e-ade4-b97f5f5ad48b",
   "metadata": {},
   "outputs": [],
   "source": [
    "EXTCLK"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "54fcff1d-fbf1-4ee1-8b0f-ed5d67882cdd",
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.trace.clock._warning_frequency = 303e6"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d95c7130-258a-4d96-a015-9c3535bbfc6c",
   "metadata": {},
   "outputs": [],
   "source": [
    "from tqdm.notebook import tnrange, tqdm\n",
    "import numpy as np\n",
    "import time\n",
    "import random\n",
    "\n",
    "TEST_CLOCK_CHANGE = True\n",
    "TEST_MUL_CHANGE = True\n",
    "\n",
    "la_skipped = 0\n",
    "setup_skipped = 0\n",
    "new_phase_skipped = 0\n",
    "phase_problems = 0\n",
    "change_problems_f = 0 \n",
    "change_problems_m = 0\n",
    "fails = 0\n",
    "passes = 0\n",
    "saved_fails = []\n",
    "all_settings = []\n",
    "freqmuls = []\n",
    "\n",
    "pbar = tqdm(total=2*REPS*3, desc='Passing')\n",
    "fbar = tqdm(total=2*REPS*3, desc='Failing')\n",
    "sbar = tqdm(total=REPS, desc='Skipped')\n",
    "\n",
    "for i in tnrange(REPS):\n",
    "    prev_settings = str(scope.clock.pll)\n",
    "    FREQ = random.randint(5e2, 20e2)*1e4 # lower resolution gets much faster setting of the CW305 PLL\n",
    "    OVERSAMP = int(300e6//FREQ)\n",
    "    if EXTCLK:\n",
    "        # in this case, the ADC clock and target-generated clock can both be shifting around relative to each other, so we need to tolerate a larger deviance:\n",
    "        mintol = 2\n",
    "    else:\n",
    "        mintol = 1\n",
    "    TOL = max(mintol, OVERSAMP//30)\n",
    "    maxmul = int(np.floor(25e6/FREQ))\n",
    "    MUL = random.randint(1, maxmul)\n",
    "    freqmuls.append([FREQ, MUL])\n",
    "    PHASE = None # setup() will pickup a random valid phase\n",
    "    try:\n",
    "        INPUT_PHASE = setup(FREQ, MUL, PHASE)\n",
    "        set_oversamp(OVERSAMP)\n",
    "        time.sleep(0.5)\n",
    "        if abs(scope.LA.sampling_clock_frequency/FREQ - OVERSAMP) / OVERSAMP * 100 > 1:\n",
    "            la_skipped += 1\n",
    "            sbar.update(1)\n",
    "            continue\n",
    "    except Exception as e:\n",
    "        print('failed to setup: %s' % e)\n",
    "        setup_skipped += 1\n",
    "        continue\n",
    "    ref_phase, ref_adc, ref_ref = get_clocks(EXTCLK)\n",
    "    ref_settings = str(scope.clock.pll)\n",
    "    ref_params = scope.clock.pll.parameters\n",
    "    ref_sampling_clock = scope.LA.sampling_clock_frequency\n",
    "\n",
    "    if ref_phase is None:\n",
    "        print('***** Could not get ref_phase')\n",
    "        continue\n",
    "    for test in ['test_clock_change', 'test_mul_change']:\n",
    "        for j in range(3):\n",
    "            try:\n",
    "                if test == 'test_clock_change':\n",
    "                    #newfreq = random.uniform(5e6, MAXCLOCK/scope.clock.adc_mul)\n",
    "                    newfreq = random.randint(5e2, MAXCLOCK/scope.clock.adc_mul//1e4)*1e4 # lower resolution gets much faster setting of the CW305 PLL\n",
    "                    newmul = MUL\n",
    "                else:\n",
    "                    newfreq = FREQ\n",
    "                    maxmul = int(np.floor(200e6/FREQ))\n",
    "                    newmul = random.randint(1, maxmul)\n",
    "                freqmuls.append([newfreq, newmul])\n",
    "                scope.LA.clkgen_enabled = False\n",
    "                setup(newfreq, newmul, 0)\n",
    "                try:\n",
    "                    setup(FREQ, MUL, INPUT_PHASE)\n",
    "                    set_oversamp(OVERSAMP)\n",
    "                except Exception as e:\n",
    "                    print('could not re-apply phase in %s: %s' % (test, e))\n",
    "                    phase_problems += 1\n",
    "                    continue\n",
    "                new_settings = str(scope.clock.pll)\n",
    "                new_params = scope.clock.pll.parameters\n",
    "                if new_params != ref_params:\n",
    "                    print('changed settings (%s); all_setting size: %d' % (test, len(all_settings)))\n",
    "                    if test == 'test_clock_change':\n",
    "                        change_problems_f += 1\n",
    "                    else:\n",
    "                        change_problems_m += 1\n",
    "                new_phase, new_adc, new_ref = get_clocks(EXTCLK)\n",
    "                new_sampling_clock = scope.LA.sampling_clock_frequency\n",
    "                if new_phase is None:\n",
    "                    new_phase_skipped += 1\n",
    "                    sbar.update(1)\n",
    "                    continue\n",
    "                if abs(new_phase - ref_phase) > TOL:\n",
    "                    print('ID %d: FREQ=%12d, OVERSAMP=%3d, TOL=%2d, MUL=%2d, INPUT_PHASE=%f' % (fails, FREQ, OVERSAMP, TOL, MUL, INPUT_PHASE), end='')\n",
    "                    if test == 'test_clock_change':\n",
    "                        print('***** Got unexpected phase %d (reference phase = %d) when changing clock frequency (%f) on iteration %d' % (new_phase, ref_phase, newfreq, j))\n",
    "                    else:\n",
    "                        print('***** Got unexpected phase %d (reference phase = %d) when changing adc_mul (%d) on iteration %d' % (new_phase, ref_phase, newmul, j))\n",
    "                    saved_fails.append(get_fail_package(fails, FREQ, OVERSAMP, TOL, MUL, INPUT_PHASE, scope.clock.adc_phase, newfreq, MUL, ref_phase, ref_adc, ref_ref, new_phase, new_adc, new_ref, ref_settings, new_settings, prev_settings, ref_sampling_clock, new_sampling_clock))\n",
    "                    fails += 1\n",
    "                    fbar.update(1)\n",
    "                else:\n",
    "                    passes += 1\n",
    "                    pbar.update(1)\n",
    "            except Exception as e:\n",
    "                new_phase_skipped += 1\n",
    "                #print('new_phase_skipped: %s' % e)\n",
    "                scope.clock.pll._registers_cached = False\n",
    "                sbar.update(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3fb073dd-0590-441b-a46d-0a55108e1672",
   "metadata": {},
   "source": [
    "Note that it's normal for the \"passing\" bar to not finish at 100%, due to \"skips\".\n",
    "\n",
    "Check that there are no failures and that we don't have \"too many\" skips:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f7551867-19e6-4929-ab02-91a0b74ee229",
   "metadata": {},
   "outputs": [],
   "source": [
    "assert fails == 0\n",
    "assert passes / (la_skipped + setup_skipped + new_phase_skipped) > 20"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "26ce72dd-49af-47d5-8f87-2825bd29fc97",
   "metadata": {},
   "source": [
    "## Check for issue #499:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "30ced9ad-81cd-4146-8f57-e748ba7aba90",
   "metadata": {},
   "outputs": [],
   "source": [
    "if EXTCLK:\n",
    "    input_dividers = 0\n",
    "    for i,s in enumerate(all_settings):\n",
    "        p = s['params']\n",
    "        muldiv = 1/p[0]*p[1]*p[2]/p[3]/p[4]\n",
    "        if muldiv != 1:\n",
    "            print('looks like issue #499: %d %f' % (i,muldiv))\n",
    "        \n",
    "        if p[0] != 1 and s['freq'] <= 100e6:\n",
    "            print('oops! %d has input divider != 1!' % i)\n",
    "            input_dividers += 1\n",
    "    assert input_dividers == 0"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5ac213af-7c33-43e3-b2ee-d1285b0aa096",
   "metadata": {},
   "source": [
    "**This concludes the test; the rest of this notebook is to help diagnose failures.**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c4b8a3ef-e391-43d9-9182-2d4a732b66d7",
   "metadata": {},
   "source": [
    "# Investigate issues found by main test loop"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dec859e6-2f5c-409e-b824-5a58e82a45e8",
   "metadata": {},
   "source": [
    "## 1. plot what we got"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fa28e888-b1ee-4fed-a8d9-279518e3b962",
   "metadata": {},
   "outputs": [],
   "source": [
    "len(saved_fails)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "01af0043-8486-4575-a4d7-9e3cd0ef7ba2",
   "metadata": {},
   "source": [
    "Pick a failure:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "faddcf71-d1b2-485a-b689-6c0635bf29f9",
   "metadata": {},
   "outputs": [],
   "source": [
    "fail_id = 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d04006df-c3eb-455e-871b-fbc974948af2",
   "metadata": {},
   "outputs": [],
   "source": [
    "saved_fails[fail_id]['ref_settings'] == saved_fails[fail_id]['new_settings']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7694e4de-2355-4ffb-9fbb-441d3ac066d7",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(saved_fails[fail_id]['prev_settings'])\n",
    "print(saved_fails[fail_id]['ref_settings'])\n",
    "print(saved_fails[fail_id]['new_settings'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "02251a5b-24cb-4ddd-a95a-41cade6c1ecb",
   "metadata": {},
   "outputs": [],
   "source": [
    "saved_fails[fail_id]['ref_settings'] == saved_fails[fail_id]['prev_settings']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "23246ff9-4661-487a-beba-09fe59cf7c8a",
   "metadata": {},
   "outputs": [],
   "source": [
    "from bokeh.plotting import figure, show\n",
    "from bokeh.resources import INLINE\n",
    "from bokeh.io import output_notebook\n",
    "import numpy as np\n",
    "\n",
    "output_notebook(INLINE)\n",
    "#output_notebook()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "95ba1572-bbd2-4c9b-9cbd-f4f4a8c92fc9",
   "metadata": {},
   "outputs": [],
   "source": [
    "samples = min(len(saved_fails[fail_id]['ref_adc']), len(saved_fails[fail_id]['new_adc']))\n",
    "xrange = list(range(samples))\n",
    "\n",
    "p = figure(width=1800)\n",
    "\n",
    "p.line(xrange, saved_fails[fail_id]['ref_ref'][:samples], line_color='blue')\n",
    "p.line(xrange, saved_fails[fail_id]['ref_adc'][:samples] - 2, line_color='red')\n",
    "\n",
    "p.line(xrange, saved_fails[fail_id]['new_ref'][:samples] - 6, line_color='green')\n",
    "p.line(xrange, saved_fails[fail_id]['new_adc'][:samples] - 4, line_color='brown')\n",
    "\n",
    "p.line(xrange, np.asarray(saved_fails[fail_id]['ref_adc'][:samples]) - np.asarray(saved_fails[fail_id]['new_adc'][:samples])- 8, line_color='black', line_width=2)\n",
    "\n",
    "show(p)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8f76c55d-03ee-454e-8576-aa1276137225",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "39b78578-e7f5-4f5a-b8d0-17d88934d27e",
   "metadata": {},
   "outputs": [],
   "source": [
    "edges = find0to1trans(saved_fails[fail_id]['ref_ref'])\n",
    "ref_edge = edges[1]\n",
    "adc_edges = find0to1trans(saved_fails[fail_id]['ref_adc'])\n",
    "print(abs(min(adc_edges, key=lambda x:abs(x-ref_edge)) - ref_edge))\n",
    "\n",
    "edges = find0to1trans(saved_fails[fail_id]['new_ref'])\n",
    "ref_edge = edges[1]\n",
    "adc_edges = find0to1trans(saved_fails[fail_id]['new_adc'])\n",
    "print(abs(min(adc_edges, key=lambda x:abs(x-ref_edge)) - ref_edge))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c49c82f1-5897-43fa-80f1-9039a9e25ed4",
   "metadata": {},
   "outputs": [],
   "source": [
    "edges = find0to1trans(saved_fails[fail_id]['ref_ref'])\n",
    "ref_edge = edges[1]\n",
    "adc_edges = find0to1trans(saved_fails[fail_id]['ref_adc'])\n",
    "print(abs(min(adc_edges, key=lambda x:abs(x-ref_edge)) - ref_edge))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d691b8a7-2bbc-4645-8e26-14dfdbc9a858",
   "metadata": {},
   "outputs": [],
   "source": [
    "ref_edge, adc_edges"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3d21ae20-67ed-4d56-917e-7ec0785aa546",
   "metadata": {},
   "outputs": [],
   "source": [
    "edges = find0to1trans(saved_fails[fail_id]['new_ref'])\n",
    "ref_edge = edges[1]\n",
    "adc_edges = find0to1trans(saved_fails[fail_id]['new_adc'])\n",
    "print(abs(min(adc_edges, key=lambda x:abs(x-ref_edge)) - ref_edge))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3694220f-106b-4017-9a37-3e9482de119a",
   "metadata": {},
   "outputs": [],
   "source": [
    "ref_edge, adc_edges"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cd67cada-5780-479c-9f7f-e268d91f41d2",
   "metadata": {},
   "outputs": [],
   "source": [
    "phase"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "53a8039f-2932-4ac2-bd46-441c5799bf80",
   "metadata": {},
   "outputs": [],
   "source": [
    "saved_fails[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5ed65db5-1264-4923-b6e0-fe143aee5ef7",
   "metadata": {},
   "source": [
    "## 2. repeat with same parameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "15be91ac-2325-49dc-94a0-bc851e226208",
   "metadata": {},
   "outputs": [],
   "source": [
    "INPUT_PHASE"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9c8f4c51-03a6-4404-b5f1-23d6e101f27f",
   "metadata": {},
   "outputs": [],
   "source": [
    "fail_id = 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "88a79d7c-2139-4d68-a53f-155a000b9e97",
   "metadata": {},
   "outputs": [],
   "source": [
    "FREQ = saved_fails[fail_id]['FREQ']\n",
    "OVERSAMP = saved_fails[fail_id]['OVERSAMP']\n",
    "MUL = saved_fails[fail_id]['MUL']\n",
    "INPUT_PHASE = saved_fails[fail_id]['INPUT_PHASE']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8c24cb2d-f90f-4f60-b1c9-06ff788eed03",
   "metadata": {},
   "outputs": [],
   "source": [
    "saved_fails[fail_id]['INPUT_PHASE'], INPUT_PHASE"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3b04b4d3-88ef-4788-ae3e-51e0d8f2b865",
   "metadata": {},
   "outputs": [],
   "source": [
    "saved_fails[fail_id]['ref_settings']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eb14816c-22c1-473e-98f6-ab643de088e9",
   "metadata": {},
   "outputs": [],
   "source": [
    "setup(FREQ, MUL, INPUT_PHASE)\n",
    "set_oversamp(OVERSAMP)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "849116e5-264d-4f6a-89f2-e41923164bc1",
   "metadata": {},
   "outputs": [],
   "source": [
    "phase, adc, ref = get_clocks(EXTCLK)\n",
    "print(phase, saved_fails[fail_id]['ref_phase'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f07996d4-5504-4dcd-829c-7edcf52a1c17",
   "metadata": {},
   "outputs": [],
   "source": [
    "offset = find0to1trans(ref)[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eb40abd6-a753-4505-ac41-e758312e24ca",
   "metadata": {},
   "outputs": [],
   "source": [
    "adc0 = adc[offset:]\n",
    "ref0 = ref[offset:]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4cfea9c0-45a0-4e74-b182-1188e2ff61be",
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.clock.adc_phase"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0bd6ae90-cef7-4ae8-92d5-725bc39315b0",
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.clock.pll"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9990e631-9584-441a-9ed7-942462db7d93",
   "metadata": {},
   "outputs": [],
   "source": [
    "NEWFREQ = saved_fails[fail_id]['NEWFREQ']\n",
    "NEWMUL = saved_fails[fail_id]['NEWMUL']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f96bc630-76fd-470f-a21a-627614853138",
   "metadata": {},
   "outputs": [],
   "source": [
    "setup(NEWFREQ, NEWMUL, 0)\n",
    "setup(FREQ, MUL, INPUT_PHASE)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "920809d0-ce7f-4dd5-831c-e61978f514b7",
   "metadata": {},
   "outputs": [],
   "source": [
    "pphase, padc, pref = get_clocks(EXTCLK)\n",
    "print(pphase)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1e63179f-c03d-4967-aeb9-a928d4730e78",
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.clock.pll"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "35683a29-a0d9-4c1c-be86-36e35df5b951",
   "metadata": {},
   "outputs": [],
   "source": [
    "saved_fails[fail_id]['new_settings']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c54a0c2f-2f91-4d5b-b3d0-031908043225",
   "metadata": {},
   "outputs": [],
   "source": [
    "saved_fails[fail_id]['ref_settings']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "64dd3fdf-2712-402d-b3ec-63285cbac067",
   "metadata": {},
   "outputs": [],
   "source": [
    "saved_fails[fail_id]['prev_settings']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a5f74151-3415-4346-8701-94e7a1ece405",
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.clock.adc_phase"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3f47e20a-6b31-44fa-81fe-e518f1928d59",
   "metadata": {},
   "outputs": [],
   "source": [
    "offset = find0to1trans(pref)[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9b1fd10d-11ca-4cae-8892-b13d7267e009",
   "metadata": {},
   "outputs": [],
   "source": [
    "adc2 = padc[offset:]\n",
    "ref2 = pref[offset:]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "514b0e54-132c-431a-a858-90fe5548f1f5",
   "metadata": {},
   "outputs": [],
   "source": [
    "samples = min(len(adc0), len(adc2))\n",
    "xrange = list(range(samples))\n",
    "\n",
    "p = figure(width=1800)\n",
    "\n",
    "p.line(xrange, ref0[:samples], line_color='blue')\n",
    "p.line(xrange, adc0[:samples] - 2, line_color='red')\n",
    "\n",
    "p.line(xrange, ref2[:samples] - 6, line_color='green')\n",
    "p.line(xrange, adc2[:samples] - 4, line_color='brown')\n",
    "\n",
    "p.line(xrange, np.asarray(adc0[:samples]) - np.asarray(adc2[:samples])- 8, line_color='black', line_width=2)\n",
    "\n",
    "show(p)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b775d00a-7a4d-4c09-9dda-65cb07d1626b",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "69710d99-c9aa-4f46-b6da-289d9e35a650",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "source": [
    "# Don't check phase: just find cases for issue #499:\n",
    "\n",
    "Runs faster than main test loop."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4035abc1-25fa-4c4a-890c-a030431f9c9c",
   "metadata": {},
   "outputs": [],
   "source": [
    "REPS = 20000\n",
    "all_settings = []\n",
    "for i in tnrange(REPS):\n",
    "    FREQ = random.randint(5e2, 20e2)*1e4 # lower resolution gets much faster setting of the CW305 PLL\n",
    "    #OVERSAMP = int(300e6//FREQ)\n",
    "    maxmul = int(np.floor(60e6/FREQ))\n",
    "    MUL = random.randint(1, maxmul)\n",
    "    freqmuls.append([FREQ, MUL])\n",
    "    try:\n",
    "        INPUT_PHASE = setup(FREQ, MUL, 0)    \n",
    "    except Exception as e:\n",
    "        print('failed to setup: %s' % e)\n",
    "        setup_skipped += 1\n",
    "        continue\n",
    "    in_div, pll_mul, fb_prescale, prescale, out_div1, out_div3 = scope.clock.pll.parameters\n",
    "    ratio = 1 / in_div * pll_mul * fb_prescale / prescale / out_div1\n",
    "    if ratio != 1:\n",
    "        print('*** Got one! iteration %d' % i)\n",
    "    #else:\n",
    "    #    print('%d ratio: %f' % (i, ratio))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python (venv39)",
   "language": "python",
   "name": "venv39"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
